package com.open.cloud.rbac.utils;

import com.alibaba.druid.util.StringUtils;
import com.jcraft.jsch.*;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.stream.FileImageInputStream;
import java.io.*;
import java.util.*;

public class SFTPUtil {
    private Logger log = LoggerFactory.getLogger(SFTPUtil.class);

    private Session session = null;

    private ChannelSftp channel = null;

    /**
     * 获取SFTP服务器连接
     *
     * @param ftpHost
     * @param ftpPort
     * @param ftpUserName
     * @param ftpPassword
     * @return
     * @throws Exception
     */
    public Map<String, String> getConnect(String ftpHost, String ftpPort, String ftpUserName, String ftpPassword)
            throws Exception {
        Map<String, String> rtnMap = new HashMap<String, String>();
        rtnMap.put("code", "0");

        // 取FTP默认端口
        if (ftpPort == null || ftpPort.equals("")) {
            ftpPort = "21";
        }
        try {
            JSch jsch = new JSch();
            //JSch.setConfig(
            //        "PreferredAuthentications",
            //        "publickey,keyboard-interactive,password");
            int port = Integer.parseInt(ftpPort);
            // 按照用户名,主机ip,端口获取一个Session对象
            session = jsch.getSession(ftpUserName, ftpHost, port);
            if (ftpPassword != null) {
                session.setPassword(ftpPassword);
            }
            Properties config = new Properties();
            // 设置不用检查hostKey
            config.put("StrictHostKeyChecking", "no");
            // UseDNS指定，sshd的是否应该看远程主机名，检查解析主机名的远程IP地址映射到相同的IP地址。
            // 默认值是 “yes” 此处是由于我们SFTP服务器的DNS解析有问题，则把UseDNS设置为“no”
            config.put("UseDNS", "no");
            config.put("PreferredAuthentications","password");
            session.setConfig(config);
            session.connect();
            // 打开SFTP通道
            channel = (ChannelSftp) session.openChannel("sftp");
            channel.connect();
            log.info("SFTP服务器连接成功！ ftpHost = " + ftpHost + ",as ftpUserName = " + ftpUserName + ", returning: " + channel);
        } catch (JSchException e) {
            rtnMap.put("code", "1");
            rtnMap.put("result", "SFTPUtil.getConnect执行出错，错误信息：" + e.toString());

            e.printStackTrace();
            log.error("SFTP服务器连接失败！" + e);
        }
        return rtnMap;
    }

    /**
     * 断开服务器连接
     *
     * @throws Exception
     */
    public Map<String, String> closeChannel() throws Exception {
        Map<String, String> rtnMap = new HashMap<String, String>();
        rtnMap.put("code", "0");

        try {
            if (channel != null) {
                channel.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
            log.error("断开SFTP服务器成功！");
        } catch (Exception e) {
            rtnMap.put("code", "1");
            rtnMap.put("result", "SFTPUtil.closeChannel执行出错，错误信息：" + e.toString());
            e.printStackTrace();
            log.error("断开SFTP服务器失败！" + e);
        }

        return rtnMap;
    }

    /**
     * 上传图片文件到服务器指定路径
     *
     * @param path           为路径+文件名称（包含文件类型）
     * @param fileName       文件名称（包含文件类型）
     * @param remoteFoldPath 指定服务器路径
     * @return
     * @throws Exception
     */
    public Map<String, String> uploadImage(String path, String fileName, String remoteFoldPath)
            throws Exception {
        Map<String, String> rtnMap = new HashMap<String, String>();
        rtnMap.put("code", "0");

        InputStream input = null;
        try {
            byte[] imgBytes = image2byte(path);
            input = new ByteArrayInputStream(imgBytes);
            // 改变当前路径到指定路径
            listFiles(remoteFoldPath);
            channel.cd(remoteFoldPath);
            channel.put(input, fileName);
        } catch (Exception e) {
            rtnMap.put("code", "1");
            rtnMap.put("result", "SFTPUtil.uploadImage执行出错，错误信息：" + e.toString());
            e.printStackTrace();
            log.error("上传文件失败！" + e);
        } finally {
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return rtnMap;
    }

    /**
     * 上传文件
     *
     * @param path           本地文件路径
     * @param fileName       上传文件名称
     * @param remoteFoldPath 上传文件目录
     * @return
     * @throws Exception
     */
    public Map<String, String> uploadFile(String path, String fileName, String remoteFoldPath)
            throws Exception {
        Map<String, String> rtnMap = new HashMap<String, String>();
        rtnMap.put("code", "0");

        InputStream input = null;
        try {
            File file = new File(path);
            input = new FileInputStream(file);
            // 改变当前路径到指定路径
            listFiles(remoteFoldPath);
            channel.cd(remoteFoldPath);
            channel.put(input, fileName);
        } catch (Exception e) {
            rtnMap.put("code", "1");
            rtnMap.put("result", "SFTPUtil.uploadFile执行出错，错误信息：" + e.toString());
            e.printStackTrace();
            log.error("上传文件失败！" + e);
        } finally {
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return rtnMap;
    }

    /**
     * 文件下载
     *
     * @param ftpPath
     * @param localPath
     * @param fileName
     * @return
     */
    public Map<String, String> downloadFile(String ftpPath, String localPath, String fileName) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        rtnMap.put("code", "0");

        try {
            File f1 = new File(localPath);

            // 如果没有的话就创建目录
            if (!f1.exists()) {
                f1.setWritable(true);
                f1.mkdirs();
                f1.setReadable(true);
            }

            String ftpFilePath = "";
            if (null != ftpPath && ftpPath.endsWith(File.separator)) {
                ftpFilePath = ftpPath + fileName;
            } else {
                ftpFilePath = ftpPath + File.separator + fileName;
            }

            String localFilePath = "";
            if (null != localPath && localPath.endsWith(File.separator)) {
                localFilePath = localPath + fileName;
            } else {
                localFilePath = localPath + File.separator + fileName;
            }
            channel.get(ftpFilePath, localFilePath);
        } catch (Exception e) {
            rtnMap.put("code", "1");
            rtnMap.put("result", "SFTPUtil.downloadFile执行出错，错误信息：" + e.toString());
            e.printStackTrace();
            log.error("SFTPUtil.downloadFile执行出错，错误信息：" + e);
        }

        return rtnMap;
    }

    /**
     * 下载单个文件
     *
     * @param remotPath：远程下载目录(以路径符号结束),结尾不带斜线
     * @param remoteFileName：下载文件名
     * @param localPath：本地保存目录(以路径符号结束),结尾不带斜线
     * @param localFileName：保存文件名
     * @return
     */
    public boolean downloadFile(String remotePath, String remoteFileName, String localPath, String localFileName) {
        // 下载文件
        Map<String, String> rtnMap = this.downloadFile(remotePath, localPath, remoteFileName);

        String code = rtnMap.get("code");

        return "0".equals(code) ? true : false;
    }

    /**
     * 批量下载文件
     *
     * @param remotePath 远程文件路径，结尾不包含分隔符"/"
     * @param localPath  本地存放路径，结尾不包含分隔符
     * @param filePrefix 文件名前缀
     * @param fileSuffix 文件名后缀
     * @param del        是否删除远程文件
     * @return
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    public List<Map<String, Object>> batchDownLoadFile(String remotePath, String localPath, String filePrefix,
                                                       String fileSuffix, boolean del) throws Exception {
        // 返回下载文件路径集合
        List<Map<String, Object>> fileRtnLn = new ArrayList<Map<String, Object>>();
        try {
            Vector v = channel.ls(remotePath);
            if (null != v) {
                int vSize = v.size();

                if (vSize > 0) {
                    Iterator it = v.iterator();
                    while (it.hasNext()) {
                        // 记录每个文件的执行情况
                        Map<String, Object> fileMap = new HashMap<String, Object>();

                        try {
                            LsEntry entry = (LsEntry) it.next();

                            // 文件名称
                            String fileName = entry.getFilename();

                            SftpATTRS attrs = entry.getAttrs();
                            if (!attrs.isDir()) {
                                if (!StringUtils.isEmpty(filePrefix) && !fileName.startsWith(filePrefix)) {
                                    continue;
                                }
                                if (!StringUtils.isEmpty(fileSuffix) && !fileName.endsWith(fileSuffix)) {
                                    continue;
                                }
                                fileRtnLn.add(fileMap);
                                // 文件名称
                                fileMap.put("fileName", fileName);

                                // 下载文件
                                Map<String, String> rtnMap = this.downloadFile(remotePath, localPath, fileName);

                                // 下载结果0成功，其他失败
                                String code = rtnMap.get("code");
                                fileMap.put("code", code);
                                fileMap.put("fileDownLoadCode", code);
                                fileMap.put("fileDownLoadResult", rtnMap.get("result"));

                                if ("0".equals(code)) {
                                    if (del) {
                                        // 删除stfp文件
                                        Map<String, String> delMap = deleteSFTP(remotePath, fileName);

                                        code = delMap.get("code");
                                        fileMap.put("code", code);
                                        fileMap.put("fileDelCode", code);
                                        fileMap.put("fileDelResult", delMap.get("result"));
                                    }
                                }
                            }
                        } catch (Exception e) {
                            fileMap.put("code", "1");
                            fileMap.put("fileDownLoadCode", "1");
                            fileMap.put("fileDownLoadResult", "SFTPUtil.batchDownLoadFile执行文件处理出错，错误信息：" + e);
                            log.error("SFTPUtil.batchDownLoadFile执行文件处理出错，错误信息：" + e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("SFTPUtil.batchDownLoadFile执行出错，错误信息：" + e.toString(), e);
            throw e;
        }
        return fileRtnLn;
    }

    /**
     * 删除stfp文件
     *
     * @param directory：要删除文件所在目录
     * @param deleteFile：要删除的文件
     * @param sftp
     */
    public Map<String, String> deleteSFTP(String directory, String deleteFile) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        rtnMap.put("code", "0");

        String filePath = directory + "/" + deleteFile;
        try {
            channel.rm(filePath);
            if (log.isInfoEnabled()) {
                log.info("SFTPUtil.deleteSFTP删除文件成功[" + filePath + "]");
            }
        } catch (Exception e) {
            rtnMap.put("code", "1");
            rtnMap.put("result", "SFTPUtil.deleteSFTP删除文件失败[" + filePath + "]，" + e.toString());
            e.printStackTrace();
            log.error("SFTPUtil.deleteSFTP删除文件失败[" + filePath + "]，" + e.toString());
        }

        return rtnMap;
    }

    /**
     * 创建目录
     *
     * @param directory
     * @return
     * @throws Exception
     */
    public boolean createDir(String directory)
            throws Exception {
        boolean isSucc = false;

        if (!isDirExist(directory)) {
            channel.mkdir(directory);

            isSucc = true;
        }

        return isSucc;
    }

    /**
     * 判断目录是否存在
     */
    public boolean isDirExist(String directory) {
        boolean isDirExistFlag = false;
        try {
            SftpATTRS sftpATTRS = channel.lstat(directory);
            return sftpATTRS.isDir();
        } catch (Exception e) {
            if (e.getMessage().toLowerCase().equals("no such file")) {
                isDirExistFlag = false;
            }
        }
        return isDirExistFlag;
    }

    /**
     * 清空文件夹
     *
     * @param remoteFoldPath
     */
    @SuppressWarnings("rawtypes")
    public Map<String, String> deleteAll(String remoteFoldPath) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        rtnMap.put("code", "0");

        try {
            if (StringUtils.isEmpty(remoteFoldPath)) {
                return rtnMap;
            }

            if (!isDirExist(remoteFoldPath)) {
                channel.mkdir(remoteFoldPath);
                return rtnMap;
            }

            if (!(remoteFoldPath.endsWith("/") || remoteFoldPath.endsWith("\\"))) {
                remoteFoldPath = remoteFoldPath + "/";
            }

            Vector vector = channel.ls(remoteFoldPath);
            int vectorSize = vector.size();

            for (int i = 0; i < vectorSize; i++) {
                ChannelSftp.LsEntry entry = (LsEntry) vector.get(i);
                if (".".equals(entry.getFilename()) || "..".equals(entry.getFilename())) {
                    continue;
                }

                channel.rm(remoteFoldPath + entry.getFilename());
            }
        } catch (Exception e) {
            rtnMap.put("code", "1");
            rtnMap.put("result", "SFTPUtil.deleteAll执行出错，错误信息：" + e.toString());
            e.printStackTrace();
        }

        return rtnMap;
    }

    /**
     * 删除指定文件
     *
     * @param remoteFoldPath 服务器路径
     * @param fileName       文件名称（包含文件类型）
     * @throws Exception
     */
    public Map<String, String> deleteFile(String remoteFoldPath, String fileName)
            throws Exception {
        Map<String, String> rtnMap = new HashMap<String, String>();
        rtnMap.put("code", "0");

        try {
            // 改变当前路径到指定路径
            listFiles(remoteFoldPath);
            channel.cd(remoteFoldPath);
            channel.rm(fileName);
        } catch (Exception e) {
            rtnMap.put("code", "1");
            rtnMap.put("result", "SFTPUtil.deleteFile执行出错，错误信息：" + e.toString());
            e.printStackTrace();
            log.error("上传删除失败！" + e);
        }

        return rtnMap;
    }

    /**
     * 把图片转换为BYTE数据
     *
     * @param path
     * @return
     */
    private byte[] image2byte(String path) {
        byte[] data = null;
        FileImageInputStream input = null;
        try {
            input = new FileImageInputStream(new File(path));
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int numBytesRead = 0;
            while ((numBytesRead = input.read(buf)) != -1) {
                output.write(buf, 0, numBytesRead);
            }
            data = output.toByteArray();
            output.close();
            input.close();
        } catch (Exception ex1) {
            log.error("变更服务器工作路径异常！" + ex1);
            ex1.printStackTrace();
        }
        return data;
    }

    /**
     * 变更服务器工作路径
     *
     * @param remotePath
     * @return
     */
    @SuppressWarnings("rawtypes")
    private Vector listFiles(String remotePath) {
        Vector vector = null;
        try {
            vector = channel.ls(remotePath);
            if (vector == null) {
                channel.mkdir(remotePath);
            }
        } catch (Exception e) {
            log.error("变更服务器工作路径异常！" + e);
            try {
                channel.mkdir(remotePath);
            } catch (SftpException e1) {
                e1.printStackTrace();
            }
        }
        return vector;
    }
}